﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using Windows.Media;
using Windows.Media.Capture;
using Windows.Storage;

using XConductor.Domain.Seedwork.Capturing;
using XConductor.Domain.Seedwork.Capturing.Exceptions;
using XConductor.Domain.Seedwork.Capturing.Settings;
using XConductor.Domain.WP8.Capturing.Settings;

namespace XConductor.Domain.WP8.Capturing
{
    public class CaptureSession : ICaptureSession
    {
        public event EventHandler<CaptureEventArgs> OnSoundLeveChanged;
        public event EventHandler<CaptureEventArgs> OnStart;
        public event EventHandler<CaptureEventArgs> OnStop;
        public event EventHandler<CaptureEventArgs> OnInterrupted;
        public event EventHandler<CaptureRuntimeExceptionEventArgs> RuntimeError;

        private MediaCapture m_mediaCapture;
        private CaptureSettings m_settings;

        private bool m_inited = false;
        private bool m_running = false;
        private bool m_interrupted = false;

        public async Task Initialize(ICaptureSettings settings)
        {
            this.m_settings = settings as CaptureSettings;

            if (this.m_settings != null && this.m_settings.IsValid() && !this.m_inited)
            {
                this.m_inited = true;

                this.AddSoundLevelChangedEventHandler();

                this.m_mediaCapture = new MediaCapture();

                await MediaCaptureInitializeAsync(this.m_mediaCapture);

                this.m_mediaCapture.RecordLimitationExceeded += this.InterruptingHandler;
                this.m_mediaCapture.Failed += this.FailingHandler;
            }
        }

        private void AddSoundLevelChangedEventHandler()
        {
            if (this.OnSoundLeveChanged != null)
            {
                MediaControl.SoundLevelChanged += (s, e) => this.OnSoundLeveChanged(s, new CaptureEventArgs(e));
            }
        }

        private async Task MediaCaptureInitializeAsync(MediaCapture mediaCapture)
        {
            if (this.m_settings.InitializationSettings == null)
            {
                await mediaCapture.InitializeAsync();
            }
            else
            {
                await mediaCapture.InitializeAsync(this.m_settings.InitializationSettings);
            }
        }

        private void FailingHandler(MediaCapture sender, MediaCaptureFailedEventArgs errorEventArgs)
        {
            if (this.RuntimeError != null)
            {
                this.RuntimeError(this, new CaptureRuntimeExceptionEventArgs(sender, new CaptureSessionRuntimeException(errorEventArgs.Message)));
            }
        }

        private void InterruptingHandler(MediaCapture mc)
        {
            if (this.OnInterrupted != null)
            {
                this.OnInterrupted(this, new CaptureEventArgs(mc));
            }
        }

        public bool Interrupted
        {
            get { return this.m_interrupted; }
        }

        public bool Running
        {
            get { return this.m_running; }
        }

        public async Task Start()
        {
            if (this.m_inited && !this.m_running)
            {
                this.m_running = true;

                await StartRecord();

                this.Started();
            }
        }

        private async Task StartRecord()
        {
            switch (this.m_settings.CaptureType)
            {
                case CaptureType.File:
                    await StartRecordToStorageFile();
                    break;
                case CaptureType.Stream:
                case CaptureType.Sink:
                default:
                    break;
            }
        }

        private async Task StartRecordToStorageFile()
        {
            await this.m_mediaCapture.StartRecordToStorageFileAsync(this.m_settings.MediaEncodingProfile, this.m_settings.OutputFile);
        }

        public async Task Stop()
        {
            if (this.m_inited && this.m_running)
            {
                this.m_running = false;

                await this.m_mediaCapture.StopRecordAsync();

                this.Stoped();
            }
        }

        private void Started()
        {
            if (this.OnStart != null)
            {
                this.OnStart(this, CaptureEventArgs.Empty);
            }
        }

        private void Stoped()
        {
            if (this.OnStop != null)
            {
                this.OnStop(this, CaptureEventArgs.Empty);
            }
        }

        public void Dispose()
        { }

        public ICaptureSettings Settings
        {
            get
            {
                return this.m_settings;
            }
        }
    }
}
